package com.eliza.base.views.learn.animation

import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.animateColor
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.animateIntOffsetAsState
import androidx.compose.animation.core.animateRect
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.style.TextMotion
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import com.eliza.base.views.learn.graphics.ShapesAnimationX
import kotlinx.coroutines.launch
import kotlin.math.roundToInt

/*
*
* 初识属性动画——使用Animator创建动画
再谈属性动画——介绍以及自定义Interpolator插值器
三谈属性动画——Keyframe以及ViewPropertyAnimator
让View具有减速效果的动画——FlingAnimation
让View具有弹性效果的动画——SpringAnimation
让View跟随状态动起来——StateListAnimator
仿抖音发布按住拍呼吸效果
Jetpack学习之——使用动画缩放图片
使用动画隐藏或显示View
Android中具有动画效果的图片资源
ViewGroup内容改变时的动画效果—LayoutTransition
在布局切换之间实现Transition动画
在Activity切换之间实现Transition动画
让转场更加酷炫——自定义Transition
*
*
*
* */

object BasicAnimationX {
    @Composable
    fun Show() {
        var swi by remember {
            mutableIntStateOf(0)
        }
        Column {
            Button(onClick = { swi = (swi + 1) % 5 }) {
                Text(text = "ShapesAnimationX ${swi}")
            }
            when (swi) {
                0 -> AnimationVisibleX()
                1 -> AnimationPosition()
                2 -> AnimationPositionX()
                3 -> AnimationTextX()
                4 -> AnimationLaunchedX()
            }
        }
    }

    @OptIn(ExperimentalAnimationApi::class)
    @Composable
    private fun AnimationVisibleX() {
        //    为背景颜色添加动画效果
        var visible by remember {
            mutableStateOf(true)
        }
        val animatedColor by animateColorAsState(
            if (visible) Color.Gray else Color.Cyan, label = "color"
        )
        //可以使用 drawBehind 修饰符开始执行绘制命令
        Column(modifier = Modifier.drawBehind {
            drawRect(animatedColor)
        }) {
            TextButton(onClick = {
                visible = !visible
            }) {
                Text(text = "Click to change $visible")
            }
            AnimatedVisibility(visible) {
                //            AnimatedVisibility 最终会从组合中移除该项
                //                Modifier.animateEnterExit() 实现自己的进入或退出过渡。
                Text(
                    text = "AnimatedVisibility 可隐藏或显示可组合项",
                    modifier = Modifier.animateEnterExit()
                )
            }
            //    alpha 只是透明隐藏
            val animatedAlpha by animateFloatAsState(
                targetValue = if (visible) 1.0f else 0.2f, label = "alpha"
            )
            Box(modifier = Modifier
                .size(100.dp)
                .graphicsLayer {
                    alpha = animatedAlpha
                }
                .clip(RoundedCornerShape(5.dp))
                .background(Color.Green)
                .align(Alignment.CenterHorizontally)) {
                Text(text = "animatedAlpha")
            }
        }
    }

    @Composable
    private fun AnimationPosition() {
        var expanded by remember { mutableStateOf(false) }
        Box(modifier = Modifier
            .background(Color.LightGray)
            //对于可组合项大小变化之间的动画，使用 animateContentSize()。
            //不能明确设置size注意：animateContentSize 在修饰符链中的位置顺序很重要。
            //为使动画流畅，  请务必将其放在任何大小修饰符（如 size 或 defaultMinSize）之前，
            //  以确保 animateContentSize 向布局报告添加动画效果之后的值更改。
            .animateContentSize()
            .height(if (expanded) 80.dp else 50.dp)
            .fillMaxWidth()
            .clickable(
                interactionSource = remember { MutableInteractionSource() }, indication = null
            ) {
                expanded = !expanded
            }) {
            Text(text = "animateContentSize")
        }

//    ========

        var moved by remember { mutableStateOf(false) }
        val pxToMoveRight = with(LocalDensity.current) {
            50.dp.toPx().roundToInt()
        }
        val pxToMoveDown = with(LocalDensity.current) {
            10.dp.toPx().roundToInt()
        }
        val offset by animateIntOffsetAsState(
            targetValue = if (moved) {
                IntOffset(pxToMoveRight, pxToMoveDown)
            } else {
                IntOffset.Zero
            }, label = "offset"
        )
//    这不会改变父项感知到的可组合项放置的位置，因此不会影响同级的放置位置。
//    这可能会导致同级元素相互重叠或在其下。偏移只会影响 Modifier.offset{} 的子布局的位置。
        Box(modifier = Modifier
            .offset {
                offset
            }
            .background(Color.Magenta)
            .size(100.dp)
            .clickable(
                interactionSource = remember { MutableInteractionSource() }, indication = null
            ) {
                moved = !moved
            })
    }

    @Composable
    private fun AnimationPositionX() {
        Column {
            var toggled by remember {
                mutableStateOf(false)
            }
            val interactionSource = remember {
                MutableInteractionSource()
            }
            Column(modifier = Modifier
                .padding(16.dp)
                .fillMaxSize()
                .clickable(indication = null, interactionSource = interactionSource) {
                    toggled = !toggled
                }) {
                val offsetTarget = if (toggled) {
                    IntOffset(50, 30)
                } else {
                    IntOffset.Zero
                }
                val offset = animateIntOffsetAsState(
                    targetValue = offsetTarget, label = "offset"
                )
                Box(
                    modifier = Modifier
                        .size(20.dp)
                        .background(Color.Blue)
                )
//        如果您想确保在为位置或大小添加动画效果时，系统不会在其他可组合项之上或之下绘制可组合项，
//        请使用 Modifier.layout{ }
                Text(text = "不会在其他可组合项之上或之下绘制可组合项 Modifier.layout")
                Box(modifier = Modifier
                    .layout { measurable, constraints ->
                        val offsetValue = if (isLookingAhead) offsetTarget else offset.value
                        val placeable = measurable.measure(constraints)
//                    如果您要在 Column 内移动 Box，并且其他子项需要在 Box 移动时移动，
//                    请使用 Modifier.layout{ } 添加偏移量信息
                        layout(
                            placeable.width + offsetValue.x,
                            placeable.height + offsetValue.y
                        ) {
                            placeable.placeRelative(offsetValue)
                        }
                    }
                    .size(100.dp)
                    .background(Color.Green))
                Box(
                    modifier = Modifier
                        .size(20.dp)
                        .background(Color.Blue)
                )
            }
            //如需为可组合项的内边距添加动画效果，请结合使用 animateDpAsState 与 Modifier.padding()：
            val animatedPadding by animateDpAsState(
                if (toggled) {
                    0.dp
                } else {
                    20.dp
                }, label = "padding"
            )
            Text(text = "可组合项的内边距添加动画效果")
            Box(modifier = Modifier
                .aspectRatio(1f)
                .fillMaxSize()
                .padding(animatedPadding)
                .background(Color(0xff53D9A1))
                .clickable(
                    interactionSource = remember { MutableInteractionSource() },
                    indication = null
                ) {
                    toggled = !toggled
                })
            Text(text = "为可组合项的高度添加动画效果")
//    如果要为阴影添加动画效果，使用 Modifier.graphicsLayer{ } 修饰符是效果更好的选项
            val mutableInteractionSource = remember {
                MutableInteractionSource()
            }
            val pressed = mutableInteractionSource.collectIsPressedAsState()
            val elevation = animateDpAsState(
                targetValue = if (pressed.value) {
                    32.dp
                } else {
                    8.dp
                }, label = "elevation"
            )
            Box(modifier = Modifier
                .size(100.dp)
                .align(Alignment.CenterHorizontally) // Column 与Row 作用域
                .graphicsLayer {
                    this.shadowElevation = elevation.value.toPx()
                }
                .clickable(interactionSource = mutableInteractionSource, indication = null) {}
                .background(Color.Green)) {
                Text(text = "阴影添加动画效果 graphicsLayer")
            }
        }
    }

    @Composable
    private fun AnimationTextX() {

        //为文本的缩放、平移或旋转添加动画效果时，请将 TextStyle 上的 textMotion 参数设置为 TextMotion.Animated
        val infiniteTransition = rememberInfiniteTransition(label = "infinite transition")
        // 颜色
        val animatedColor by infiniteTransition.animateColor(
            initialValue = Color(0xFF60DDAD),
            targetValue = Color(0xFF4285F4),
            animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse),
            label = "color"
        )
        //缩放
        val scale by infiniteTransition.animateFloat(
            initialValue = 0.3f,
            targetValue = 1f,
            animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse),
            label = "scale"
        )
        Column {

            Text(text = "rememberInfiniteTransition 无线循环动画")
            Text(text = "Modifier.graphicsLayer 可平移、旋转或缩放文本。",
                modifier = Modifier
                    .graphicsLayer {
                        scaleX = scale
                        scaleY = scale
                        transformOrigin = TransformOrigin.Center
                    }
                    .align(Alignment.CenterHorizontally),
                color = animatedColor,
                // Text composable does not take TextMotion as a parameter.
                // Provide it via style argument but make sure that we are copying from current theme
                style = LocalTextStyle.current.copy(textMotion = TextMotion.Animated))
            Text(
                text = "animatedColor 可平移、旋转或缩放文本。",
                modifier = Modifier.align(Alignment.CenterHorizontally),
                color = animatedColor,
                // Text composable does not take TextMotion as a parameter.
                // Provide it via style argument but make sure that we are copying from current theme
                style = LocalTextStyle.current.copy(textMotion = TextMotion.Animated)
            )
            //结合使用 rememberInfiniteTransition 和 infiniteRepeatable animationSpec 可持续重复播放动画
            val infiniteTransitionX = rememberInfiniteTransition(label = "infinite")
            val color by infiniteTransitionX.animateColor(
                initialValue = Color.Green,
                targetValue = Color.Blue,
                animationSpec = infiniteRepeatable(
                    animation = tween(1000, easing = LinearEasing),
                    // 以指定其切换方式
                    repeatMode = RepeatMode.Reverse,
                ),
                //使用 finiteRepeatable 可重复指定次数。
                label = "repeatMode"
            )
            Column(
                modifier = Modifier.drawBehind {
                    drawRect(color)
                }
            ) {
                Text(text = "repeatMode")
            }
        }
    }


    @Composable
    private fun AnimationLaunchedX() {
        Column {
            Text(text = "依序")
            val alphaAnimation = remember { Animatable(0f) }
            val yAnimation = remember { Animatable(0f) }
            //使用 Animatable 协程 API 执行依序或并发动画。
            // 连续对 Animatable 调用 animateTo 会导致每个动画等待前面的动画完成后再继续。这是因为它属于挂起函数。
            LaunchedEffect("animationKey") {
                alphaAnimation.animateTo(1f)
                yAnimation.animateTo(100f)
                yAnimation.animateTo(500f, animationSpec = tween(100))
            }
            Text(text = "并发")
            // 使用协程 API（Animatable#animateTo() 或 animate）或 Transition API 来实现并发动画
            LaunchedEffect("animationKey2") {
                launch {
                    alphaAnimation.animateTo(1f)
                }
                launch {
                    yAnimation.animateTo(100f)
                }
            }
            //=========
//        可以使用 updateTransition API 使用相同的状态同时驱动许多不同的属性动画
            var currentState by remember { mutableStateOf(BoxState.Collapsed) }
            val transition = updateTransition<BoxState>(currentState, label = "transition")
            val rect by transition.animateRect(label = "rect") { state ->
                when (state) {
                    BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f)
                    BoxState.Expanded -> Rect(100f, 100f, 300f, 300f)
                }
            }
            val borderWidth by transition.animateDp(label = "borderWidth") { state ->
                when (state) {
                    BoxState.Collapsed -> 1.dp
                    BoxState.Expanded -> 0.dp
                }
            }
            Box(modifier = Modifier.border(borderWidth, Color.Blue)) {
                Text(text = "borderWidth")
            }
        }
    }
    /*

    * 下面总结了不同的 animationSpec 选项：
    spring：基于物理特性的动画，所有动画的默认设置。您可以更改刚度或阻尼比，以实现不同的动画外观和风格。
    tween（“Between”的缩写）：基于时长的动画，使用 Easing 函数在两个值之间添加动画效果。
    keyframes：用于指定动画中某些关键点处的值的规范。
    repeatable：基于时长的规范，运行一定次数，由 RepeatMode 指定。
    infiniteRepeatable：基于时长的规范，无限期运行。
    snap：即时贴靠结束值，而不显示任何动画。
    *
    * */


}



